/*
 * SwingGraphics.java  0.4.0 / Oct 26, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.swing;

import com.cute4j.swing.graphics.BasicStroke;
import com.cute4j.swing.graphics.Color;
import com.cute4j.swing.graphics.Font;
import com.cute4j.swing.graphics.FontMetrics;
import com.cute4j.swing.graphics.GradientPaint;
import com.cute4j.swing.graphics.Graphics;
import com.cute4j.swing.graphics.Image;
import com.cute4j.swing.graphics.ImageProducer;
import com.cute4j.swing.graphics.Paint;
import com.cute4j.swing.graphics.PathIterator;
import com.cute4j.swing.graphics.Shape;
import com.cute4j.swing.graphics.Stroke;
import com.cute4j.swing.graphics.TexturePaint;

/**
 * Swing Graphics.
 *
 * @cute4j.native
 *    generate="header"
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
final class SwingGraphics extends Graphics {

// Package-Private Fields. -----------------------------------------------------

  Color color;
  Font font;
  boolean fontSet;
  FontMetrics fontMetrics;
  Paint paint;
  Stroke stroke;
  SwingImage.SwingImageConsumer imageConsumer;

// Package-Private "Native" Fields. --------------------------------------------

  long _graphicsPointer;

// Package-Private Constructors. -----------------------------------------------

  SwingGraphics(long graphicsPointer) {
    _graphicsPointer = graphicsPointer;
  }

// Overridden Methods. ---------------------------------------------------------

  /**
   * {@inheritDoc}
   */
  public Color getColor() {
    if (color == null) {
      color = _getColor(_graphicsPointer);
    }
    return color;
  }

  /**
   * {@inheritDoc}
   */
  public void setColor(Color color) {
    if (color != null) {
      _setColor(_graphicsPointer, color.getARGB());
      this.color = color;
      paint = color;
    }
  }

  /**
   * {@inheritDoc}
   */
  public Font getFont() {
    if (font == null) {
      font = _getFont(_graphicsPointer);
    }
    return font;
  }

  /**
   * {@inheritDoc}
   */
  public void setFont(Font font) {
    if (!fontSet && (this.font != null)) {
      this.font.dispose();
    }
    fontSet = (font != null);
    _setFont(_graphicsPointer, font);
    this.font = font;
    if (fontMetrics != null) {
      fontMetrics.dispose();
    }
    fontMetrics = null;
  }

  /**
   * {@inheritDoc}
   */
  public FontMetrics getFontMetrics() {
    if (fontMetrics == null) {
      fontMetrics = new FontMetrics(getFont());
    }
    return fontMetrics;
  }

  /**
   * {@inheritDoc}
   */
  public Paint getPaint() {
    if (paint == null) {
      paint = getColor();
    }
    return paint;
  }

  /**
   * {@inheritDoc}
   */
  public void setPaint(Paint paint) {
    if (paint instanceof Color) {
      setColor((Color)paint);
    } else if (paint instanceof GradientPaint) {
      GradientPaint gp = (GradientPaint)paint;
      Color c1 = gp.getColor1();
      Color c2 = gp.getColor2();
      _setLinearGradient(_graphicsPointer, gp.getX1(), gp.getY1(), c1.getARGB(),
                         gp.getX2(), gp.getY2(), c2.getARGB(), gp.isCyclic());
    } else if (paint instanceof TexturePaint) {
      final Paint oldPaint = this.paint;
      TexturePaint texturePaint;
      if (oldPaint instanceof TexturePaint) {
        texturePaint = (TexturePaint)oldPaint;
        ((SwingImage)texturePaint.getImage()).releaseImagePointer();
      }
      texturePaint = (TexturePaint)paint;
      _setTextureGradient(_graphicsPointer,
                          ((SwingImage)texturePaint.getImage()).
                                                            getImagePointer());
    }
    this.paint = paint;
  }

  /**
   * {@inheritDoc}
   */
  public Stroke getStroke() {
    if (stroke == null) {
      stroke = new BasicStroke();
    }
    return stroke;
  }

  /**
   * {@inheritDoc}
   */
  public void setStroke(Stroke stroke) {
    if (stroke instanceof BasicStroke) {
      BasicStroke bs = (BasicStroke)stroke;
      _setBasicStroke(_graphicsPointer, bs.getLineWidth(), bs.getEndCap(),
                      bs.getLineJoin(), bs.getMiterLimit(), bs.getDashArray(),
                      bs.getDashPhase());
    }
    this.stroke = stroke;
  }

  /**
   * {@inheritDoc}
   */
  public void draw(Shape shape) {
    if (_graphicsPointer != 0) {
      _drawPath(_graphicsPointer, createPainterPath(shape.getPathIterator()));
      // Optimization: painter path is allocated on the stack
    }
  }

  /**
   * {@inheritDoc}
   */
  public void drawLine(int x1, int y1, int x2, int y2) {
    // (_graphicsPointer != 0) checked by native code
    _drawLine(_graphicsPointer, x1, y1, x2, y2);
  }

  /**
   * {@inheritDoc}
   */
  public void drawRect(int x, int y, int width, int height) {
    // (_graphicsPointer != 0) checked by native code
    _drawRect(_graphicsPointer, x, y, width, height);
  }

  /**
   * {@inheritDoc}
   */
  public void drawOval(int x, int y, int width, int height) {
    // (_graphicsPointer != 0) checked by native code
    _drawOval(_graphicsPointer, x, y, width, height);
  }

  /**
   * {@inheritDoc}
   */
  public void drawString(String str, int x, int y) {
    // (_graphicsPointer != 0) checked by native code
    _drawString(_graphicsPointer, str, x, y);
  }

  /**
   * {@inheritDoc}
   */
  public void drawImage(Image image, int x, int y) {
    // (_graphicsPointer != 0) checked by native code
    if (image != null) {
      if (image.getClass() == SwingImage.class) {
        _drawImage(_graphicsPointer, ((SwingImage)image).getImagePointer(), x,
                   y);
      } else {
        ImageProducer imageProducer = image.getSource();
        SwingImage.SwingImageConsumer imageConsumer = this.imageConsumer;
        if (imageConsumer == null) {
          imageConsumer = new SwingImage.SwingImageConsumer();
          this.imageConsumer = imageConsumer;
        }
        imageProducer.startProduction(imageConsumer);
        _drawImage(_graphicsPointer, imageConsumer.pixels, imageConsumer.length,
                   x, y);
        imageConsumer.pixels = null;
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  public void fill(Shape shape) {
    if (_graphicsPointer != 0) {
      _fillPath(_graphicsPointer, createPainterPath(shape.getPathIterator()));
      // Optimization: painter path is allocated on the stack
    }
  }

  /**
   * {@inheritDoc}
   */
  public void fillRect(int x, int y, int width, int height) {
    // (_graphicsPointer != 0) checked by native code
    _fillRect(_graphicsPointer, x, y, width, height);
  }

  /**
   * {@inheritDoc}
   */
  public void fillOval(int x, int y, int width, int height) {
    // (_graphicsPointer != 0) checked by native code
    _fillOval(_graphicsPointer, x, y, width, height);
  }

  /**
   * {@inheritDoc}
   */
  protected void disposeObject() {
    if (fontMetrics != null) {
      fontMetrics.dispose();
    }
    if (!fontSet && (font != null)) {
      font.dispose();
    }
    _graphicsPointer = 0;
    // Optimization: Native graphics is allocated on the stack
  }

// Package-Private Static Methods. ---------------------------------------------

  static long createPainterPath(PathIterator pi) {
    long painterPathPointer = _createPainterPath();
    _setPainterFillRule(painterPathPointer, pi.getWindingRule());
    int[] coords = new int[6];
    while (!pi.isDone()) {
      switch (pi.currentSegment(coords)) { // NOPMD
        case PathIterator.SEG_MOVETO:
          _painterPathMoveTo(painterPathPointer, coords[0], coords[1]);
          break;
        case PathIterator.SEG_LINETO:
          _painterPathLineTo(painterPathPointer, coords[0], coords[1]);
          break;
        case PathIterator.SEG_QUADTO:
          _painterPathQuadTo(painterPathPointer, coords[0], coords[1],
                             coords[2], coords[3]);
          break;
        case PathIterator.SEG_CUBICTO:
          _painterPathCubicTo(painterPathPointer, coords[0], coords[1],
                              coords[2], coords[3], coords[4], coords[5]);
          break;
        case PathIterator.SEG_CLOSE:
          _painterPathClose(painterPathPointer);
          break;
      }
      pi.next();
    }
    return painterPathPointer;
  }

// Package-Private Native Methods. ---------------------------------------------

  native Color _getColor(long graphicsPointer);

  native void _setColor(long graphicsPointer, int argb);

  native Font _getFont(long graphicsPointer);

  native void _setFont(long graphicsPointer, Font font);

  native void _setLinearGradient(long graphicsPointer, int x1, int y1,
                                 int argb1, int x2, int y2, int argb2,
                                 boolean cyclic);

  native void _setTextureGradient(long graphicsPointer,  long imagePointer);

  native void _setBasicStroke(long graphicsPointer, float width, int cap,
                              int join, float miterLimit, float[] dash,
                              float dashPhase);

  native void _drawPath(long graphicsPointer, long painterPathPointer);

  native void _drawLine(long graphicsPointer, int x1, int y1, int x2, int y2);

  native void _drawRect(long graphicsPointer, int x, int y, int width,
                        int height);

  native void _drawOval(long graphicsPointer, int x, int y, int width,
                        int height);

  native void _drawString(long graphicsPointer, String str, int x, int y);

  native void _drawImage(long graphicsPointer, long imagePointer, int x, int y);

  native void _drawImage(long graphicsPointer, byte[] imageData, int length,
                         int x, int y);

  native void _fillPath(long graphicsPointer, long painterPathPointer);

  native void _fillRect(long graphicsPointer, int x, int y, int width,
                        int height);

  native void _fillOval(long graphicsPointer, int x, int y, int width,
                        int height);

  // Painter Path Methods. -----------------------------------------------------

  static native long _createPainterPath();

  static native void _setPainterFillRule(long painterPathPointer, int fillRule);

  static native void _painterPathMoveTo(long painterPathPointer, int x, int y);

  static native void _painterPathLineTo(long painterPathPointer, int x, int y);

  static native void _painterPathQuadTo(long painterPathPointer, int cx, int cy,
                                        int ex, int ey);

  static native void _painterPathCubicTo(long painterPathPointer, int c1x,
                                         int c1y, int c2x, int c2y, int ex,
                                         int ey);

  static native void _painterPathClose(long painterPathPointer);

}
